#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include "device.h"
#include "board.h"
#include "input_dev.h"
#include "driver/gpio.h"
#include "esp_log.h"
#include "esp_timer.h"
#include "util.h"

#define TAG "GPIO_KEY"

struct gpio_key_map {
	uint16_t key_code;
    gpio_num_t gpio_num;
	bool active_level;
};

struct gpio_key_data {
    esp_timer_handle_t timer;

	uint32_t scan_count;
	uint16_t prev_keycode;
	uint16_t prev_stable_keycode;

	input_notify_t notify;
};

struct gpio_key_config {

	uint16_t poll_interval_ms;
	uint16_t sample_filter_dep;

	uint16_t key_cnt;
	const struct gpio_key_map *key_maps;
};

static uint16_t gpio_key_get_keycode(const struct gpio_key_config *cfg)
{
    const struct gpio_key_map *map = cfg->key_maps;
    
    for (int i = 0; i < cfg->key_cnt; i++) {
        if (gpio_get_level(map->gpio_num) == map->active_level) {
            return map->key_code;
        }
    
        map++;
    }
    
    return KEY_RESERVED;
}

static void gpio_key_report(struct gpio_key_data *gpio_key, int key_code, int value)
{
    struct input_value val;

    if (gpio_key->notify) {
        val.type = EV_KEY;
        val.code = key_code;
        val.value = value;

        gpio_key->notify(NULL, &val);
    }
}

static void gpio_key_esp_poll(void* arg)
{
    struct device *dev = arg;
	const struct gpio_key_config *cfg = dev->config->config_info;
	struct gpio_key_data *gpio_key = dev->driver_data;

	uint16_t keycode;

	/* get keycode */
	keycode = gpio_key_get_keycode(cfg);

	// printf("keycode: %d\n", keycode);

	/* no key is pressed or releasing */
	if (keycode == KEY_RESERVED &&
	    gpio_key->prev_stable_keycode == KEY_RESERVED)
		return;

	if (keycode == gpio_key->prev_keycode) {
		gpio_key->scan_count++;
		if (gpio_key->scan_count == cfg->sample_filter_dep) {
			/* previous key is released? */
			if (gpio_key->prev_stable_keycode != KEY_RESERVED && keycode != gpio_key->prev_stable_keycode) {
                gpio_key_report(gpio_key, gpio_key->prev_stable_keycode, 0);
            }
			
			/* a new key press? */
			if (keycode != KEY_RESERVED) {
                gpio_key_report(gpio_key, keycode, 1);
            }

			/* clear counter for new checking */
			gpio_key->prev_stable_keycode = keycode;
			gpio_key->scan_count = 0;
		}
	} else {
		/* new key pressed? */
		gpio_key->prev_keycode = keycode;
		gpio_key->scan_count = 0;
	}
}

static void gpio_key_enable(struct device *dev)
{
	const struct gpio_key_config *cfg = dev->config->config_info;
	struct gpio_key_data *gpio_key = dev->driver_data;

	ESP_LOGI(TAG, "enable gpio_key");

    esp_timer_start_periodic(gpio_key->timer, cfg->poll_interval_ms * 1000);
}

static void gpio_key_disable(struct device *dev)
{
	struct gpio_key_data *gpio_key = dev->driver_data;

	ESP_LOGI(TAG, "disable gpio_key");

    esp_timer_stop(gpio_key->timer);
}

static void gpio_key_register_notify(struct device *dev, input_notify_t notify)
{
	struct gpio_key_data *gpio_key = dev->driver_data;

	ESP_LOGI(TAG, "register notify");

	gpio_key->notify = notify;
}

static void gpio_key_unregister_notify(struct device *dev, input_notify_t notify)
{
	struct gpio_key_data *gpio_key = dev->driver_data;

	ESP_LOGI(TAG, "unregister notify");

	gpio_key->notify = NULL;
}

const struct input_device_api gpio_key_driver_api = {
	.enable = gpio_key_enable,
	.disable = gpio_key_disable,
	.register_notify = gpio_key_register_notify,
	.unregister_notify = gpio_key_unregister_notify,
};

static int gpio_key_esp_init(struct device *dev)
{
	const struct gpio_key_config *cfg = dev->config->config_info;
	struct gpio_key_data *gpio_key = dev->driver_data;

	ESP_LOGI(TAG, "init gpio_key");

    gpio_config_t gpio_cfg;

    for (int i = 0; i < cfg->key_cnt; i++) {
        memset(&gpio_cfg, 0, sizeof(gpio_cfg));
        gpio_cfg.pin_bit_mask = 1 << cfg->key_maps[i].gpio_num;
        gpio_cfg.mode = GPIO_MODE_INPUT;
        if (cfg->key_maps[i].active_level) {
            gpio_cfg.pull_up_en = 0;
            gpio_cfg.pull_down_en = 1;
        } else {
            gpio_cfg.pull_up_en = 1;
            gpio_cfg.pull_down_en = 0;
        }
        gpio_cfg.intr_type = GPIO_INTR_DISABLE;
        gpio_config(&gpio_cfg);
    }

    esp_timer_create_args_t timer_args = {
       .callback = (esp_timer_cb_t)gpio_key_esp_poll,
       .arg = (void* )dev,
       .dispatch_method = ESP_TIMER_TASK,
       .name = "gpio_key_poll",
       .skip_unhandled_events = true
    };
    esp_timer_create(&timer_args, &gpio_key->timer);

	return 0;
}

static const struct gpio_key_map gpio_key_acts_keymaps[] = {
#ifdef BOARD_GPIO_KEY_MAPPINGS
	BOARD_GPIO_KEY_MAPPINGS
#endif
};

static struct gpio_key_data gpio_key_esp_ddata;

static const struct gpio_key_config gpio_key_esp_cdata = {
	.poll_interval_ms = 20,
	.sample_filter_dep = 3,

	.key_cnt = ARRAY_SIZE(gpio_key_acts_keymaps),
	.key_maps = &gpio_key_acts_keymaps[0],
};

DEVICE_DEFINE(gpio_key, "gpio_key", gpio_key_esp_init, &gpio_key_esp_cdata, &gpio_key_driver_api, &gpio_key_esp_ddata, 
			PRE_KERNEL_1, 30);
